/*
  1.元素像素缓动动画函数，支持属性值单位为px的动画
  参数1：目标元素
  参数2：预期效果, 类型为对象
    example: {
      width: 90, // 目标属性值，位移动画必须保证元素已定位
      fps: 60, // 刷新帧率，一秒刷新多少次，1~1000
      range: 20 // 幅度，每次刷新划分动画的份额数量
    }
  参数3：回调函数，可链式执行下一动画
  参数4：定时器标识，可忽略
*/
function pixelSlow (element, attribute, callback, timeID = null) {
  // 清除上一次调用生成的定时器
  timeID && clearInterval(timeID)
  // 获取目标属性名
  const targetKey = Object.keys(attribute)[0]
  // 获取目标属性值
  const targetAttr = Object.values(attribute)[0]
  // 部署动画定时器
  timeID = setInterval(() => {
    // 获取当前属性值
    const currentAttr = Number(window.getComputedStyle(element)[targetKey])
    // 计算步长
    let stepPx = (targetAttr - currentAttr) / attribute.range
    // 对步长取整，确保最后步骤为1或-1
    stepPx = stepPx > 0 ? Math.ceil(stepPx) : Math.floor(stepPx)
    // 计算下一步属性值
    const nextAttr = currentAttr + stepPx
    // 更新元素属性值
    element.style[targetKey] = `${nextAttr}px`
    // 判断动画是否结束
    if (nextAttr === targetAttr) {
      // 清除定时器
      clearInterval(timeID)
      // 执行回调函数
      typeof callback === 'function' && callback()
    }
  }, 1000 / attribute.fps)
}

/*
  2.元素旋转缓动动画函数
  参数1：目标元素
  参数2：预期效果, 类型为对象
    example: {
      rotate: 90, // 目标旋转角度
      fps: 60, // 刷新帧率，一秒刷新多少次，1~1000
      range: 20 // 幅度，每次刷新划分动画的份额数量
    }
  参数3：回调函数，可链式执行下一动画
  参数4：定时器标识，可忽略
*/
function rotateSlow (element, attribute, callback, timeID = null) {
  // 清除上一次调用生成的定时器
  timeID && clearInterval(timeID)
  // 获取目标旋转角度
  const targetDeg = attribute.rotate
  // 部署动画定时器
  timeID = setInterval(() => {
    // 获取元素当前目标属性字符串
    const style = window.getComputedStyle(element)
    const transformString =
      style['-webkit-transform'] || style['-moz-transform'] || style.transform
    // 声明当前旋转角度，默认为0
    let currentDeg = 0
    if (transformString !== 'none') {
      // 获取当前角度系数数组
      const values = transformString
        .split('(')[1]
        .split(')')[0]
        .split(',')
      // 获取当前旋转角度
      currentDeg = Math.round(
        // 计算坐标轴中（x, y）点与x轴正方向夹角角度
        Math.atan2(values[1], values[0]) * (180 / Math.PI)
      )
      // 归正当前旋转角度
      currentDeg < 0 && (currentDeg = 360 + currentDeg)
    }
    // 计算步长，正数顺时针旋转，负数逆时针旋转
    let stepDeg = (targetDeg - currentDeg) / attribute.range
    // 对步长取整，确保最后步骤为1或-1
    stepDeg = stepDeg > 0 ? Math.ceil(stepDeg) : Math.floor(stepDeg)
    // 计算下一步角度
    let nextDeg = currentDeg + stepDeg
    // 强制最后一步到位
    if (nextDeg === Math.ceil(targetDeg)) {
      nextDeg = targetDeg
    }
    // 更新元素旋转角度
    element.style.transform = `rotate(${nextDeg}deg)`
    // 判断动画是否结束
    if (nextDeg === targetDeg) {
      // 清除定时器
      clearInterval(timeID)
      // 执行回调函数
      typeof callback === 'function' && callback()
    }
  }, 1000 / attribute.fps)
}

/*
  3.元素透明度缓动动画函数
  参数1：目标元素
  参数2：预期效果, 类型为对象
    example: {
      opacity/backgroundColor: 1, // 元素属性透明度或背景色透明度，取值范围0~1
      fps: 60, // 刷新帧率，一秒刷新多少次，必须0~1000之间
      range: 20, // 幅度，每次刷新划分动画的份额数量
    }
  参数3：回调函数，可链式执行下一动画
  参数4：定时器标识，可忽略
*/
function alphaSlow (element, attribute, callback, timeID = null) {
  // 清除上一次调用生成的定时器
  timeID && clearInterval(timeID)
  // 获取目标属性名
  const targetKey = Object.keys(attribute)[0]
  // 获取目标透明度，放大一百倍
  const targetAlpha = Object.values(attribute)[0] * 100
  // 部署元素属性透明度动画
  if (targetKey === 'opacity') {
    // 部署动画定时器
    timeID = setInterval(() => {
      // 获取当前透明度，放大一百倍
      const currentAlpha =
        Number(window.getComputedStyle(element)[targetKey]) * 100
      // 计算步长
      let stepOpacity = (targetAlpha - currentAlpha) / attribute.range
      // 对步长取整，确保最后步骤为1或-1
      stepOpacity =
        stepOpacity > 0 ? Math.ceil(stepOpacity) : Math.floor(stepOpacity)
      // 计算下一步透明度
      const nextAlpha = currentAlpha + stepOpacity
      // 更新元素透明度
      element.style[targetKey] = nextAlpha / 100
      // 判断动画是否结束
      if (nextAlpha === targetAlpha) {
        // 清除定时器
        clearInterval(timeID)
        // 执行回调函数
        typeof callback === 'function' && callback()
      }
    }, 1000 / attribute.fps)
  } else {
    // 部署背景色透明度动画
    timeID = setInterval(() => {
      // 获取当前透明度字符串
      const currentAlphaStr = window.getComputedStyle(element)[targetKey]
      // 解析字符串获取三原色数组
      const alphaArr = currentAlphaStr.match(/\d{1,3}/g)
      // 当前透明度
      const currentAlpha = alphaArr[3] ? Number(alphaArr[3]) * 100 : 100
      // 计算步长
      let stepRgba = (targetAlpha - currentAlpha) / attribute.range
      // 对步长取整，确保最后步骤为1或-1
      stepRgba = stepRgba > 0 ? Math.ceil(stepRgba) : Math.floor(stepRgba)
      // 计算下一步透明度
      const nextAlpha = currentAlpha + stepRgba
      // 更新元素透明度
      element.style[targetKey] = `rgba(${alphaArr[0]}, ${alphaArr[1]}, ${
        alphaArr[2]
      }, ${nextAlpha / 100})`
      // 判断动画是否结束
      if (nextAlpha === targetAlpha) {
        // 清除定时器
        clearInterval(timeID)
        // 执行回调函数
        typeof callback === 'function' && callback()
      }
    }, 1000 / attribute.fps)
  }
}

/*
  4.元素颜色缓动动画函数
  参数1：目标元素
  参数2：预期效果, 类型为对象
    example: {
      color/backgroundColor: #fff, // 字体或背景色，可选值为rgb(), rgba(), #十六进制，类型为字符串
      fps: 60, // 刷新帧率，一秒刷新多少次，必须0~1000之间
      range: 20, // 幅度，每次刷新划分动画的份额数量
    }
  参数3：回调函数，可链式执行下一动画
  参数4：定时器标识，可忽略
*/
function colorSlow (element, attribute, callback, timeID = null) {
  // 清除上一次调用生成的定时器
  timeID && clearInterval(timeID)
  // 获取目标属性名
  const targetKey = Object.keys(attribute)[0]
  // 获取目标颜色字符串
  const targetString = Object.values(attribute)[0]
  // 声明三原色和透明度
  let targetRed, targetGreen, targetBlue, targetAlpha
  if (/#/.test(targetString)) {
    // 十六进制
    const red =
      targetString.length > 4
        ? targetString.slice(1, 2)
        : targetString.charAt(1) + targetString.charAt(1)
    const green =
      targetString.length > 4
        ? targetString.slice(3, 4)
        : targetString.charAt(2) + targetString.charAt(2)
    const blue =
      targetString.length > 4
        ? targetString.slice(5, 6)
        : targetString.charAt(3) + targetString.charAt(3)
    targetRed = Number('0x' + red)
    targetGreen = Number('0x' + green)
    targetBlue = Number('0x' + blue)
    targetAlpha = 100
  } else {
    // rgba/rgb
    // 解析字符串获取三原色
    const target = targetString.match(/\d{1,3}/g)
    targetRed = target[0]
    targetGreen = target[1]
    targetBlue = target[2]
    targetAlpha = (target[3] || 1) * 100
  }
  // 目标颜色数组
  const targetArr = [targetRed, targetGreen, targetBlue, targetAlpha]
  // 部署元素字体颜色动画
  timeID = setInterval(() => {
    // 获取当前颜色
    const currentColor = window.getComputedStyle(element)[targetKey]
    // 解析字符串获取三原色
    const current = currentColor.match(/\d{1,3}/g)
    // 当前三原色和透明度
    const currentRed = current[0]
    const currentGreen = current[1]
    const currentBlue = current[2]
    const currentAlpha = (current[3] || 1) * 100
    // 当前颜色数组
    const currentArr = [currentRed, currentGreen, currentBlue, currentAlpha]
    // 步长数组
    let stepRed, stepGreen, stepBlue, stepAlpha
    const stepArr = [stepRed, stepGreen, stepBlue, stepAlpha]
    // 下一步颜色数组
    let nextRed, nextGreen, nextBlue, nextAlpha
    const nextArr = [nextRed, nextGreen, nextBlue, nextAlpha]
    // 定义下一步颜色
    stepArr.forEach((stepValue, index) => {
      // 计算步长
      stepValue = (targetArr[index] - currentArr[index]) / attribute.range
      // 对步长取整，确保最后步骤为1或-1
      stepValue = stepValue > 0 ? Math.ceil(stepValue) : Math.floor(stepValue)
      // 计算下一步颜色
      nextArr[index] = currentArr[index] + stepValue
    })
    // 更新元素透明度
    element.style[targetKey] = `rgba(${nextArr[0]}, ${nextArr[1]}, ${
      nextArr[2]
    }, ${nextArr[3] / 100})`
    // 判断动画是否结束
    if (nextArr.join('') === targetArr.join('')) {
      // 清除定时器
      clearInterval(timeID)
      // 执行回调函数
      typeof callback === 'function' && callback()
    }
  }, 1000 / attribute.fps)
}

// 导出函数
export { pixelSlow, rotateSlow, alphaSlow, colorSlow }
